/**
 * Project: core.common
 * 
 * File Created at 2016年4月4日
 * 
 * Copyright 2015-2015 dx.com Croporation Limited.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * DongXue software Company. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with dx.com.
 */
package com.dx.pf.commons.reflect;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;

import org.springframework.util.FastByteArrayOutputStream;

import com.dx.pf.commons.io.IOUtil;
import com.dx.pf.commons.utils.StringUtil;

/** 
* @ClassName: ObjectUtil 
* @Description: 有关object的工具类 
* @author wuzhenfang(wzfbj2008@163.com)
* @date 2016年4月4日 上午2:02:53 
* @version V1.0 
*/
public class ObjectUtil {
	
	private static final Class<?>[] BASIC_NUMBER_CLASSES = new Class[] {
            short.class, int.class, long.class, float.class, double.class };
    /**
     * 访问对象成员属性值值
     * @param obj
     * @param field
     * @return
     * @throws YichaException
     */
    @SuppressWarnings("rawtypes")
    private static final Object getFieldValue(Object obj, String field) throws Exception {
        Object result = null;
        if (obj instanceof Map) {
            return ((Map) obj).get(field);
        }

        if (obj == null) {
            return null;
        }

        Method getterMethod = null;
        try {
            getterMethod = obj.getClass().getMethod("get" + StringUtil.capitalize(field));
        } catch (Exception e) {
        }
        if (getterMethod == null) {
            try {
                getterMethod = obj.getClass().getMethod("is" + StringUtil.capitalize(field));
            } catch (Exception e) {
            }
        }
        if (getterMethod == null) {
            Field privateField;
            try {
                privateField = obj.getClass().getDeclaredField(field);
                privateField.setAccessible(true);
                result = privateField.get(obj);
            } catch (Exception e) {
                throw new Exception("field[" + field + "] doesn't exist.");
            }
        } else {
            try {
                result = getterMethod.invoke(obj);
            } catch (Exception e) {
            }
        }
        return result;
    }

    /**
     * 获取对象属性值
     * @param obj 被取值的对象
     * @param clazz  返回值的类型
     * @param path 格式:field1.field2.field3
     * @param <T> a T object.
     * @return a T object.
     */
    @SuppressWarnings("unchecked")
    public static final <T> T getValue(Object obj, Class<T> clazz, String path) {
        Object o = getValue(obj, path);
        return o == null ? null : (T) o;
    }

    /**
     * <p> getValue. </p>
     * @param obj a {@link java.lang.Object} object.
     * @param path a {@link java.lang.String} object.
     * @return a {@link java.lang.Object} object.
     */
    public static final Object getValue(Object obj, String path) {
        if (obj == null || !StringUtil.isNotBlank(path)) {
            return null;
        }
        String[] arr = path.split(".");
        Object o = obj;
        for (int i = 0, len = arr.length; i < len; i++) {
            final String field = StringUtil.strip(arr[i],null);
            try {
                o = getFieldValue(o, field);
            } catch (Exception e) {
                o = null;
            }
        }
        return o;
    }

    /**
     * 判断是否是数字类型
     *
     * @param obj
     *            a {@link java.lang.Object} object.
     * @return a boolean.
     */
    public static final boolean isNumberType(Object obj) {
        if (obj == null) {
            throw new RuntimeException("object is null.");
        }
        if (obj instanceof Number) {
            return true;
        } else {
            for (Class<?> clazz : BASIC_NUMBER_CLASSES) {
                if (obj.getClass().equals(clazz)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 判断对象是否为零
     *
     * @param obj
     *            a {@link java.lang.Object} object.
     * @return a boolean.
     */
    public static final boolean isZero(Object obj) {
        if (!isNumberType(obj)) {
            return false;
        }
        final String foo = String.valueOf(obj);
        return StringUtil.equals(foo, "0") || StringUtil.equals(foo, "0.0");
    }

    /**
     * 设置对象属性
     * @param object a {@link java.lang.Object} object.
     * @param field a {@link java.lang.String} object.
     * @param value a T object.
     * @param paramType a {@link java.lang.Class} object.
     * @param <T> a T object.
     */
    @SuppressWarnings("rawtypes")
    public static final <T> void setValue(final Object object, final String field, final T value, final Class paramType) {
        try {
            Method md = object.getClass().getMethod("set" + StringUtil.capitalize(field), paramType);
            if (md != null) {
                md.invoke(object, value);
            }
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 设置对象属性
     *
     * @param object
     *            a {@link java.lang.Object} object.
     * @param field
     *            a {@link java.lang.String} object.
     * @param value
     *            a T object.
     * @param <T>
     *            a T object.
     */
    public static final <T> void setValue(final Object object, final String field, final T value) {
        try {
            for (Method method : object.getClass().getMethods()) {
                if (StringUtil.equals(method.getName(), "set" + StringUtil.capitalize(field))) {
                    method.invoke(object, value);
                    break;
                }
            }
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }
    
	/**
	 * <p>判断对象是否为空</p>
	 * 
	 * <pre>
	 * null				-> true
	 * ""				-> true
	 * " "				-> true
	 * "null"			-> true
	 * empty Collection	-> true
	 * empty Array		-> true
	 * others			-> false
	 * </pre>
	 * @param object
	 * @return
	 */
	public static boolean isEmpty(Object object){
		if(object == null){
			return true;
		}else if(object instanceof String){
			return StringUtil.isEmpty((String)object);
		}else if(object instanceof Collection<?>){
			return ((Collection<?>)object).isEmpty();
		}else if(object instanceof Map<?,?>){
			return ((Map<?,?>)object).isEmpty();
		}else if(object.getClass().isArray()){
			return Array.getLength(object) == 0;
		}else{
			return false;
		}
	}
	
	/**
	 * <p>判断对象是否是数组</p>
	 * 
	 * @param obj
	 * @return
	 */
	public static boolean isArray(Object object) {
		return (object != null && object.getClass().isArray());
	}
	
	/**
	 * <p>如果object为null则返回defaultValue</p>
	 * 
	 * @param object
	 * @param defaultValue
	 * @return
	 */
	public static <T> T defaultIfNull(T object, T defaultValue) {
        return object != null ? object : defaultValue;
    }
	
	/**
	 * <p>Null-safe 调用Object.toString()方法</p>
	 * 
	 * @param object
	 * @return
	 */
	public static String toString(Object object) {
        return object == null ? "" : object.toString();
    }
	
	/**
	 * <p>Null-safe 找出一组对象中的最小值</p>
	 * 
	 * @param values
	 * @return	如果所有元素都不为空则返回不为null的&最小的那个元素,否则返回null
	 */
	@SafeVarargs
	public static <T extends Comparable<? super T>> T min(T... values) {
        T result = null;
        if (values != null) {
            for (T value : values) {
                if (compare(value, result, true) < 0) {
                    result = value;
                }
            }
        }
        return result;
    }
	
	/**
	 * <p>Null-safe 找出一组对象中的最大值</p>
	 * 
	 * @param values
	 * @return 如果所有元素都不为空则返回不为null的&最大的那个元素,否则返回null
	 */
	@SafeVarargs
	public static <T extends Comparable<? super T>> T max(T... values) {
        T result = null;
        if (values != null) {
            for (T value : values) {
                if (compare(value, result, false) > 0) {
                    result = value;
                }
            }
        }
        return result;
    }
	
	/**
	 * <p>Null-safe 比较两个对象
	 * (如果c1、c2中存在null值,则认为其为最小值)</p>
	 * 
	 * @param c1			- 第一个比较参数,可能为空
	 * @param c2			- 第二个比较参数,可能为空
	 * @return				- 负数：c1 > c2,0：c1 = c2,正数c1 > c2
	 */
	public static <T extends Comparable<? super T>> int compare(T c1, T c2) {
        return compare(c1, c2, false);
    }
	
	/**
	 * <p>Null-safe 比较两个对象</p>
	 * 
	 * @param c1			- 第一个比较参数,可能为空
	 * @param c2			- 第二个比较参数,可能为空 
	 * @param nullGreater	- 如果为true则认为null是较大的
	 * @return				- 负数：c1 > c2,0：c1 = c2,正数c1 > c2
	 */
	public static <T extends Comparable<? super T>> int compare(T c1, T c2, boolean nullGreater) {
        if (c1 == c2) {
            return 0;
        } else if (c1 == null) {
            return nullGreater ? 1 : -1;
        } else if (c2 == null) {
            return nullGreater ? -1 : 1;
        }
        return c1.compareTo(c2);
    }
	
	/**
	 * 比较两个对象是否相等。<br>
	 * 相同的条件有两个，满足其一即可：<br>
	 * 1. obj1 == null && obj2 == null; 2. obj1.equals(obj2)
	 * 
	 * @param obj1 对象1
	 * @param obj2 对象2
	 * @return 是否相等
	 */
	public static boolean equals(Object obj1, Object obj2) {
		return (obj1 != null) ? (obj1.equals(obj2)) : (obj2 == null);
	}

	/**
	 * 计算对象长度，如果是字符串调用其length函数，集合类调用其size函数，数组调用其length属性，其他可遍历对象遍历计算长度
	 * 
	 * @param obj 被计算长度的对象
	 * @return 长度
	 */
	public static int length(Object obj) {
		if (obj == null) {
			return 0;
		}
		if (obj instanceof CharSequence) {
			return ((CharSequence) obj).length();
		}
		if (obj instanceof Collection) {
			return ((Collection<?>) obj).size();
		}
		if (obj instanceof Map) {
			return ((Map<?, ?>) obj).size();
		}

		int count;
		if (obj instanceof Iterator) {
			Iterator<?> iter = (Iterator<?>) obj;
			count = 0;
			while (iter.hasNext()) {
				count++;
				iter.next();
			}
			return count;
		}
		if (obj instanceof Enumeration) {
			Enumeration<?> enumeration = (Enumeration<?>) obj;
			count = 0;
			while (enumeration.hasMoreElements()) {
				count++;
				enumeration.nextElement();
			}
			return count;
		}
		if (obj.getClass().isArray() == true) {
			return Array.getLength(obj);
		}
		return -1;
	}

	/**
	 * 对象中是否包含元素
	 * 
	 * @param obj 对象
	 * @param element 元素
	 * @return 是否包含
	 */
	public static boolean contains(Object obj, Object element) {
		if (obj == null) {
			return false;
		}
		if (obj instanceof String) {
			if (element == null) {
				return false;
			}
			return ((String) obj).contains(element.toString());
		}
		if (obj instanceof Collection) {
			return ((Collection<?>) obj).contains(element);
		}
		if (obj instanceof Map) {
			return ((Map<?, ?>) obj).values().contains(element);
		}

		if (obj instanceof Iterator) {
			Iterator<?> iter = (Iterator<?>) obj;
			while (iter.hasNext()) {
				Object o = iter.next();
				if (equals(o, element)) {
					return true;
				}
			}
			return false;
		}
		if (obj instanceof Enumeration) {
			Enumeration<?> enumeration = (Enumeration<?>) obj;
			while (enumeration.hasMoreElements()) {
				Object o = enumeration.nextElement();
				if (equals(o, element)) {
					return true;
				}
			}
			return false;
		}
		if (obj.getClass().isArray() == true) {
			int len = Array.getLength(obj);
			for (int i = 0; i < len; i++) {
				Object o = Array.get(obj, i);
				if (equals(o, element)) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * 检查对象是否为null
	 * 
	 * @param obj 对象
	 * @return 是否为null
	 */
	public static boolean isNull(Object obj) {
		return null == obj;
	}

	/**
	 * 检查对象是否不为null
	 * 
	 * @param obj 对象
	 * @return 是否为null
	 */
	public static boolean isNotNull(Object obj) {
		return null != obj;
	}
	
	/**
	 * 克隆对象<br>
	 * 对象必须实现Serializable接口
	 * @param obj 被克隆对象
	 * @return 克隆后的对象
	 * @throws Exception 
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public static <T extends Cloneable> T clone(T obj) throws Exception {
		return ClassUtil.invoke(obj, "clone");
	}

	/**
	 * 克隆对象<br>
	 * 对象必须实现Serializable接口
	 * 
	 * @param obj 被克隆对象
	 * @return 克隆后的对象
	 * @throws Exception 
	 * @throws ClassNotFoundException
	 */
	@SuppressWarnings("unchecked")
	public static <T> T clone(T obj) throws Exception {
		final FastByteArrayOutputStream byteOut = new FastByteArrayOutputStream();
		ObjectOutputStream out = null;
		try {
			out = new ObjectOutputStream(byteOut);
			out.writeObject(obj);
			out.flush();
			final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(byteOut.toByteArray()));
			return (T) in.readObject();
		} catch (Exception e) {
			throw new  Exception(e);
		} finally {
			IOUtil.close(out);
		}
	}

	/**
	 * 序列化<br>
	 * 对象必须实现Serializable接口
	 * 
	 * @param <T>
	 * @param t 要被序列化的对象
	 * @return 序列化后的字节码
	 * @throws Exception 
	 */
	public static <T> byte[] serialize(T t) throws Exception {
		FastByteArrayOutputStream byteOut = new FastByteArrayOutputStream();
		ObjectOutputStream oos = null;
		try {
			oos = new ObjectOutputStream(byteOut);
			oos.writeObject(t);
			oos.flush();
		} catch (Exception e) {
			throw new Exception(e);
		} finally {
			IOUtil.close(oos);
		}
		return byteOut.toByteArray();
	}

	/**
	 * 反序列化<br>
	 * 对象必须实现Serializable接口
	 * 
	 * @param <T>
	 * @param bytes 反序列化的字节码
	 * @return 反序列化后的对象
	 * @throws Exception 
	 */
	@SuppressWarnings("unchecked")
	public static <T> T unserialize(byte[] bytes) throws Exception {
		ObjectInputStream ois = null;
		try {
			ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
			ois = new ObjectInputStream(bais);
			return (T) ois.readObject();
		} catch (Exception e) {
			throw new Exception(e);
		}
	}

	/**
	 * 是否为基本类型，包括包装类型和非包装类型
	 * @see ClassUtil#isBasicType(Class)
	 * @param object 被检查对象
	 * @return 是否为基本类型
	 */
	public static boolean isBasicType(Object object){
		return ClassUtil.isBasicType(object.getClass());
	}
	
	/**
	 * 检查是否为有效的数字<br>
	 * 检查Double和Float是否为无限大，或者Not a Number<br>
	 * 非数字类型和Null将返回true
	 * @param obj 被检查类型
	 * @return 检查结果，非数字类型和Null将返回true
	 */
	public static boolean isValidIfNumber(Object obj) {
		if (obj != null && obj instanceof Number) {
			if (obj instanceof Double) {
				if (((Double) obj).isInfinite() || ((Double) obj).isNaN()) {
					return false;
				}
			} else if (obj instanceof Float) {
				if (((Float) obj).isInfinite() || ((Float) obj).isNaN()) {
					return false;
				}
			}
		}
		return true;
	}
}
